package com.uduemc.biso.node.web.api.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.uduemc.biso.core.extities.center.Host;
import com.uduemc.biso.core.extities.pojo.NginxSSL;
import com.uduemc.biso.core.extities.pojo.NginxServerConf;
import com.uduemc.biso.core.property.NginxHeaderProperty;
import com.uduemc.biso.core.utils.OSinfoUtil;
import com.uduemc.biso.node.core.common.entities.SSL;
import com.uduemc.biso.node.core.common.feign.CSSLFeign;
import com.uduemc.biso.node.core.entities.HDomain;
import com.uduemc.biso.node.core.entities.HRepertory;
import com.uduemc.biso.node.core.property.GlobalProperties;
import com.uduemc.biso.node.core.utils.SitePathUtil;
import com.uduemc.biso.node.web.api.component.RequestHolder;
import com.uduemc.biso.node.web.api.service.DomainService;
import com.uduemc.biso.node.web.api.service.HostService;
import com.uduemc.biso.node.web.api.service.HostSetupService;
import com.uduemc.biso.node.web.api.service.NginxService;
import com.uduemc.biso.node.web.component.NginxOperate;
import com.uduemc.biso.node.web.component.NginxServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.File;
import java.io.IOException;
import java.net.IDN;
import java.util.ArrayList;
import java.util.List;

@Service
public class NginxServiceImpl implements NginxService {

    @Autowired
    NginxServer nginxServer;

    @Autowired
    RequestHolder requestHolder;

    @Autowired
    HostSetupService hostSetupServiceImpl;

    @Autowired
    HostService hostServiceImpl;

    @Autowired
    DomainService domainServiceImpl;

    @Autowired
    NginxOperate nginxOperate;

    @Autowired
    CSSLFeign cSSLFeign;

    @Autowired
    GlobalProperties globalProperties;

    @Override
    public boolean bindDomain(HDomain hDomain) throws IOException {
        return bindDomain(hDomain, true);
    }

    @Override
    public boolean bindDomain(HDomain hDomain, boolean reload) throws IOException {
        if (hDomain == null || hDomain.getHostId() == null || StrUtil.isBlank(hDomain.getDomainName())) {
            return false;
        }
        Long hostId = hDomain.getHostId();
        Host host = hostServiceImpl.getInfoById(hostId);
        if (host == null) {
            return false;
        }
        return bindDomain(host, hDomain, reload);
    }

    @Override
    public boolean bindDomain(long hostId) throws IOException {
        Host host = hostServiceImpl.getInfoById(hostId);
        if (host == null) {
            return false;
        }
        List<HDomain> listHDomain = domainServiceImpl.findByHostIdDomainTypeStatus(hostId);
        if (CollectionUtils.isEmpty(listHDomain)) {
            return true;
        }
        for (HDomain hDomain : listHDomain) {
            if (!bindDomain(host, hDomain, false)) {
                return false;
            }
        }
        return nginxOperate.boolReload();
    }

    @Override
    public boolean bindDomain(Host host, HDomain hDomain) throws IOException {
        return bindDomain(host, hDomain, true);
    }

    @Override
    public boolean bindDomain(Host host, HDomain hDomain, boolean reload) throws IOException {
        NginxServerConf nginxServerConf = makeNginxServerConf(hDomain, host);
        return bindDomain(host, nginxServerConf, reload);
    }

    @Override
    public boolean bindDomain(Host host, NginxServerConf nginxServerConf) throws IOException {
        return bindDomain(host, nginxServerConf, true);
    }

    @Override
    public boolean bindDomain(Host host, NginxServerConf nginxServerConf, boolean reload) throws IOException {
        return nginxServer.bindDomain(host, nginxServerConf, reload);
    }

    @Override
    public NginxServerConf makeNginxServerConf(String domainName) {
        Host host = requestHolder.getHost();
        HDomain hDomain = null;
        if (host == null) {
            try {
                hDomain = domainServiceImpl.getInfoByDomainName(domainName);
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                host = hostServiceImpl.getInfoById(hDomain.getHostId());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if (hDomain == null || host == null) {
            return null;
        }
        return makeNginxServerConf(hDomain, host);
    }

    @Override
    public NginxServerConf makeNginxServerConf(HDomain hDomain, Host host) {
        if (host == null || hDomain == null) {
            return null;
        }
        Long hostId = host.getId();
        Long domainId = hDomain.getId();
        if (hostId == null || domainId == null) {
            return null;
        }

        String domainName = hDomain.getDomainName();

        int rate = 128;
        int bandwidth = hostSetupServiceImpl.getBandwidth(hostId);
        if (bandwidth > 0) {
            rate = (int) (bandwidth / 10 * 1024 / 8);
        } else {
            rate = -1;
        }

        if (rate > 100) {
            rate = -1;
        }

        String limitReq = null;
        if (bandwidth > 80 && bandwidth <= 100) {
            limitReq = "zone=req_zone40 burst=150";
        } else if (bandwidth > 50 && bandwidth <= 80) {
            limitReq = "zone=req_zone20 burst=100";
        } else if (bandwidth > 20 && bandwidth <= 50) {
            limitReq = "zone=req_zone10 burst=50";
        } else if (bandwidth <= 20) {
            limitReq = "zone=req_zone3 burst=30";
        }

        String basePath = globalProperties.getSite().getBasePath();
        String userPathByCode = SitePathUtil.getUserPathByCode(basePath, host.getRandomCode()) + "/";

        NginxSSL nginxSSL = null;
        SSL ssl = null;
        try {
            ssl = domainServiceImpl.infoSSL(hDomain);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (ssl != null) {
            nginxSSL = makeNginxSSL(basePath, ssl, host);
        }

        // 添加绑定域名的同时增加Nginx相关域名的配置服务
        List<String> addHeader = makeAddHeader(ssl != null);

        NginxServerConf nginxServerConf = new NginxServerConf();
        nginxServerConf.setServerName(IDN.toASCII(domainName)).setSsl(nginxSSL).setAddHeader(addHeader).setLimitRate(rate > 0 ? (rate + "K") : null)
                .setLimitReq(limitReq).setHostBasePath(userPathByCode);

        if (OSinfoUtil.isWindows()) {
            nginxServerConf.setGlobalAccessLog(globalProperties.getNginx().getBase() + File.separator + "logs" + File.separator + "access.log");
            nginxServerConf.setGlobalErrorLog(globalProperties.getNginx().getBase() + File.separator + "logs" + File.separator + "error.log");
        } else {
            nginxServerConf.setGlobalAccessLog("/htdocs/nginxlog/access.log");
            nginxServerConf.setGlobalErrorLog("/htdocs/nginxlog/error.log");
        }

        return nginxServerConf;
    }

    protected static List<String> makeAddHeader(boolean ssl) {
        List<NginxHeaderProperty.NginxAddHeader> nginxAddHeaders = NginxHeaderProperty.defaultHeader(ssl);
        if (CollUtil.isEmpty(nginxAddHeaders)) {
            return null;
        }
        List<String> list = new ArrayList<>();
        for (NginxHeaderProperty.NginxAddHeader addHeader : nginxAddHeaders) {
            list.add(addHeader.getHeader().addHeader() + (addHeader.isAlways() ? " always" : ""));
        }
        return list;
    }

//    public static void main(String[] arges) {
//        List<String> strings = makeAddHeader(true);
//        if (CollUtil.isNotEmpty(strings))
//            strings.forEach(item -> System.out.println(item));
//    }

    protected NginxSSL makeNginxSSL(String basePath, SSL ssl, Host host) {
        if (ssl == null) {
            return null;
        }
        if (ssl.getHssl().getId() == null) {
            return null;
        }
        if (ssl.getCertificate() == null || ssl.getPrivatekey() == null) {
            return null;
        }
        HRepertory privatekey = ssl.getPrivatekey();
        HRepertory certificate = ssl.getCertificate();
        String privatekeypath = SitePathUtil.getUserNginxSSLPathByCode(basePath, host.getRandomCode()) + "/" + privatekey.getOriginalFilename();
        String certificatepath = SitePathUtil.getUserNginxSSLPathByCode(basePath, host.getRandomCode()) + "/" + certificate.getOriginalFilename();

        NginxSSL nginxSSL = new NginxSSL();
        nginxSSL.setSslCertificateKey(privatekeypath).setSslCertificate(certificatepath);
        return nginxSSL;
    }

}
